/*
 * Copyright 2025 Haulmont.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.jmix.masquerade.component;

import com.codeborne.selenide.CheckResult;
import com.codeborne.selenide.SelenideElement;
import io.jmix.masquerade.condition.DateValue;
import io.jmix.masquerade.condition.SpecificCondition;
import io.jmix.masquerade.condition.TimeValue;
import org.openqa.selenium.By;

import static com.codeborne.selenide.Condition.exactValue;
import static com.codeborne.selenide.Selenide.$;
import static io.jmix.masquerade.JConditions.*;
import static io.jmix.masquerade.JSelectors.byChained;
import static io.jmix.masquerade.sys.TagNames.INPUT;
import static org.openqa.selenium.By.cssSelector;

/**
 * Web-element wrapper for date time picker. Supports setting date and time values,
 * {@link DateValue}, {@link TimeValue} condition checking.
 */
public class DateTimePicker extends AbstractField<DateTimePicker> {

    public static final By DATE_PICKER_BY = cssSelector("[slot='date-picker']");
    public static final By TIME_PICKER_BY = cssSelector("[slot='time-picker']");

    protected static final String ARIA_LABELLEDBY_ATTRIBUTE = "aria-labelledby";

    public DateTimePicker(By by) {
        super(by);
    }

    @Override
    public CheckResult check(SpecificCondition condition) {
        if (condition instanceof DateValue dateValue) {
            getDateInputDelegate().shouldHave(exactValue(dateValue.getValue()));
            return CheckResult.accepted();
        } else if (condition instanceof TimeValue timeValue) {
            getTimeInputDelegate().shouldHave(exactValue(timeValue.getValue()));
            return CheckResult.accepted();
        }

        return super.check(condition);
    }

    /**
     * Sets the value to the date picker input field part.
     *
     * @param dateValue date value as a string presentation value
     * @return {@code this} to call fluent API
     */
    public DateTimePicker setDateValue(String dateValue) {
        getDateInputDelegate()
                .shouldBe(VISIBLE)
                .shouldBe(ENABLED)
                .shouldNotBe(READONLY)
                .setValue(dateValue)
                .pressEnter();

        return this;
    }

    /**
     * Sets the value to the time picker input field part.
     *
     * @param timeValue time value as a string presentation value
     * @return {@code this} to call fluent API
     */
    public DateTimePicker setTimeValue(String timeValue) {
        getTimeInputDelegate()
                .shouldBe(VISIBLE)
                .shouldBe(ENABLED)
                .shouldNotBe(READONLY)
                .setValue(timeValue)
                .pressEnter();

        return this;
    }

    protected SelenideElement getDateInputDelegate() {
        return getDelegate().find(byChained(DATE_PICKER_BY, INPUT));
    }

    protected SelenideElement getTimeInputDelegate() {
        return getDelegate().find(byChained(TIME_PICKER_BY, INPUT));
    }

    @Override
    protected SelenideElement getLabelDelegate() {
        // required to find label element using `aria-labelledby` DOM attribute due to
        // multiple inner label elements (for inner date-picker and time-picker)
        return $(byChained(
                by,
                cssSelector("[id='%s']".formatted(getAriaLabelledByAttribute()))
        ));
    }

    protected String getAriaLabelledByAttribute() {
        return getDelegate().getDomAttribute(ARIA_LABELLEDBY_ATTRIBUTE);
    }
}
